package com.xsq.audio;

import android.content.ComponentName;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;

import androidx.annotation.NonNull;

import com.xsq.audio.player.bean.AudioPositionBean;
import com.xsq.audio.player.utils.LogUtil;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Consumer;
import io.reactivex.rxjava3.schedulers.Schedulers;
import io.reactivex.rxjava3.subjects.PublishSubject;

/**
 * 与服务器通信的控制器
 * Created by Shiquan.Xiao on 2023/3/29.
 */
public class AudioController implements IAudioController {

    /**
     * 播放进度订阅
     */
    private final PublishSubject<AudioPositionBean> onPlayingPosBus = PublishSubject.create();
    /**
     * 连接浏览器服务器的接口
     */
    private IServiceConnection connection;
    private final AudioServiceConnection.Builder connectionBuilder;
    private final ControllerResultReceiver controllerResultReceiver =  new ControllerResultReceiver(null);
    /**
     * 控制器与服务器通信的回调接口
     */
    private final List<IAudioControllerListener> controllerListenerList = new ArrayList<>();
    private final List<IAudioSubscriptionCallback> subscriptionCallbackList = new ArrayList<>();
    /**
     * 与服务通信控制器
     */
    private MediaControllerCompat audioController;
    /**
     * 控制Exoplayer播放器
     */
    private MediaControllerCompat.TransportControls transportControls;
    /**
     * 音频数据列表
     */
    private List<AudioItemData> curAudioItemList;
    /**
     * 音频播放持续时间信息
     */
    private final AudioPositionBean positionBean = new AudioPositionBean();
    /**
     * 当前播放状态
     */
    private PlaybackStateCompat playingState;
    /**
     * 当前播放数据项
     */
    private MediaMetadataCompat curPlayingData;
    private Disposable playingPosDisposable;
    private String curRootId = AudioDataSource.BROWSER_ROOT_ID;
    private int curItemIndex = 0;
    private int repeatMode = PlaybackStateCompat.REPEAT_MODE_INVALID;
    private int shuffleMode = PlaybackStateCompat.SHUFFLE_MODE_INVALID;

    public AudioController(Builder builder) {
        curAudioItemList = builder.audioItemList;
        connectionBuilder = builder.connectionBuilder;

        setListener();
    }

    public static class Builder {

        private final AudioServiceConnection.Builder connectionBuilder;
        private List<AudioItemData> audioItemList;

        public Builder(Context context) {
            connectionBuilder = new AudioServiceConnection.Builder(context)
                    .setServiceComponentName(new ComponentName(context, AudioService.class));
        }

        public Builder audioItemList(List<AudioItemData> audioItemList) {
            this.audioItemList = audioItemList;
            return this;
        }

        public AudioController build() {
            return new AudioController(this);
        }

    }

    /**
     * 连接媒体浏览器服务
     */
    @Override
    public void startService() {
        connection = connectionBuilder.build();
        connection.connect();
    }

    @Override
    public void setAudioItemList(String rootId, List<AudioItemData> audioItemList) {
        if (isConnected()) {
            this.curAudioItemList = audioItemList;
            sendAudioPlayData(rootId, audioItemList);
        }
    }

    /**
     * 订阅指定专辑，目前默认是一个根专辑为/，为后期需要扩展有多个专辑
     *
     * @param rootId   专辑id
     */
    @Override
    public void subscribe(String rootId, IAudioSubscriptionCallback callback) {
        if (callback == null) return;
        if (isConnected()) {
            if (!subscriptionCallbackList.contains(callback)) {
                subscriptionCallbackList.add(callback);
            }
            connection.subscribeBrowserService(rootId);
        }
    }

    /**
     * 一对一订阅和取消订阅
     *
     * @param rootId   专辑id
     */
    @Override
    public void unsubscribe(String rootId, IAudioSubscriptionCallback callback) {
        if (callback == null) return;
        if (isConnected()) {
            subscriptionCallbackList.remove(callback);
            connection.unsubscribeBrowserService(rootId);
        }
    }

    /**
     * 播放媒体选项
     */
    @Override
    public void playAudioItem(AudioItemData data) {
        if (isConnected()) {
            String audioId = data.getAudioId();
            if (isPrepared(playingState) && curPlayingData != null) {
                String mediaId = curPlayingData.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID);
                if (audioId.equals(mediaId)) {
                    if (isPlayEnabled(playingState)) {
                        transportControls.play();
                    } else {
                        LogUtil.m("playing error, audioId: " + audioId);
                    }
                    return;
                }
            }
            transportControls.playFromMediaId(audioId, null);
        }
    }

    @Override
    public void playAudioId(String audioId) {
        if (isConnected()) {
            if (isPrepared(playingState) && curPlayingData != null) {
                String mediaId = curPlayingData.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID);
                if (audioId.equals(mediaId)) {
                    if (isPlayEnabled(playingState)) {
                        transportControls.play();
                    } else {
                        LogUtil.m("playing error, audioId: " + audioId);
                    }
                    return;
                }
            }
            transportControls.playFromMediaId(audioId, null);
        }
    }

    /**
     * 向浏览器服务添加歌曲列表
     */
    @Override
    public void sendAudioPlayData(String rootId, List<AudioItemData> audioItemList) {
        curRootId = rootId;
        Bundle extra = new Bundle();
        extra.putSerializable(AudioDataSource.EXTRA_ROOT_ID_DATA, (Serializable) audioItemList);
        audioController.sendCommand(rootId, extra, controllerResultReceiver);
    }

    /**
     * 播放正在执行音频项
     */
    @Override
    public void play() {
        if (isPrepared(playingState)) {
            if (isPlayEnabled(playingState)) {
                transportControls.play();
            }
        }
    }

    /**
     * 暂停正在执行音频项
     */
    @Override
    public void pause() {
        if (playingState != null && playingState.getState() == PlaybackStateCompat.STATE_PLAYING) {
            transportControls.pause();
        }
    }

    @Override
    public boolean isPlaying() {
        return isPlaying(playingState);
    }

    /**
     * 停止正在执行音频项
     */
    @Override
    public void stop() {
        if (isPrepared(playingState)) {
            transportControls.stop();
            playingState = null;
        }
    }

    @Override
    public void close() {
        if (isConnected()) {
            audioController.sendCommand(AudioDataSource.BROWSER_CONTROLLER_CLOSE, null, null);
        }
    }

    /**
     * 下一首
     */
    @Override
    public void nextAudio() {
        if (isConnected()) {
            if (repeatMode == PlaybackStateCompat.REPEAT_MODE_ONE) {
                //由于单曲循环模式，列表最后一首不能跳到下一首，所以根据id跳转
                List<MediaSessionCompat.QueueItem> queueItems = audioController.getQueue();
                if (curItemIndex == queueItems.size() - 1) {
                    transportControls.skipToQueueItem(queueItems.get(0).getQueueId());
                    return;
                }

            }

            transportControls.skipToNext();
        }
    }

    /**
     * 上一首
     */
    @Override
    public void previousAudio() {
        if (isConnected()) {
            if (repeatMode == PlaybackStateCompat.REPEAT_MODE_ONE) {
                //由于单曲循环模式，列表第一首不能跳到最后一首，所以根据id跳转
                if (curItemIndex == 0) {
                    List<MediaSessionCompat.QueueItem> queueItems = audioController.getQueue();
                    int size = queueItems.size();
                    if (size > 0) {
                        transportControls.skipToQueueItem(queueItems.get(size-1).getQueueId());
                        return;
                    }
                }

            }

            transportControls.skipToPrevious();
        }
    }

    @Override
    public void seekTo(int mc) {
        if (isConnected()) {
            transportControls.seekTo(mc);
        }
    }

    @Override
    public void setRepeatMode(int repeatMode) {
        if (isConnected()) {
            transportControls.setRepeatMode(repeatMode);
        }
    }

    @Override
    public void setShuffleMode(int shuffleMode) {
        if (isConnected()) {
            transportControls.setShuffleMode(shuffleMode);
        }
    }

    @Override
    public int getRepeatMode() {
        return repeatMode;
    }

    @Override
    public Observable<AudioPositionBean> onPlayingPosBus() {
        return onPlayingPosBus;
    }

    @Override
    public MediaMetadataCompat getCurPlayingData() {
        return curPlayingData;
    }

    @Override
    public List<AudioItemData> getCurAudioItemList() {
        return curAudioItemList;
    }

    @Override
    public void addIAudioControllerListener(IAudioControllerListener listener) {
        this.controllerListenerList.add(listener);
    }

    @Override
    public void removeIAudioControllerListener(IAudioControllerListener listener) {
        this.controllerListenerList.remove(listener);
    }

    @Override
    public int getCurrentItemPosition() {
        return curItemIndex;
    }

    /**
     * 是否连接浏览器
     */
    @Override
    public boolean isConnected() {
        boolean isConnect = audioController != null && connection != null && connection.isConnected();
        if (isConnect) {
            LogUtil.e("audio controller not connect the browser service");
        }
        return isConnect;
    }

    /**
     * 是否准备
     */
    private boolean isPrepared(PlaybackStateCompat state) {
        if (state == null) return false;
        return state.getState() == PlaybackStateCompat.STATE_BUFFERING |
                state.getState() == PlaybackStateCompat.STATE_PLAYING |
                state.getState() == PlaybackStateCompat.STATE_PAUSED;
    }

    /**
     * 是否正在播放
     */
    private boolean isPlaying(PlaybackStateCompat state) {
        if (state == null) return false;
        return state.getState() == PlaybackStateCompat.STATE_BUFFERING |
                state.getState() == PlaybackStateCompat.STATE_PLAYING;
    }

    /**
     * 是否可播放
     */
    private boolean isPlayEnabled(PlaybackStateCompat state) {
        if (state == null) return false;
        return ((state.getActions() & PlaybackStateCompat.ACTION_PLAY) != 0L) |
                (((state.getActions() & PlaybackStateCompat.ACTION_PLAY_PAUSE) != 0L) &&
                        (state.getState() == PlaybackStateCompat.STATE_PAUSED));
    }

    /**
     * 检查音频播放进度
     */
    private void checkPlaybackPosition() {
        playingPosDisposable = Observable.interval(500L, TimeUnit.MILLISECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnNext(new Consumer<Long>() {
                    @Override
                    public void accept(Long aLong) throws Throwable {
                        if (playingState == null || curPlayingData == null) {
                            closePlayingPos();
                            return;
                        }

                        long curPos;
                        if (playingState.getState() == PlaybackStateCompat.STATE_PLAYING) {
                            long timeDelta = SystemClock.elapsedRealtime() - playingState.getLastPositionUpdateTime();
                            curPos = playingState.getPosition() + (timeDelta * (long) playingState.getPlaybackSpeed());
                        } else {
                            curPos = playingState.getPosition();
                        }
                        long duration = curPlayingData.getLong(MediaMetadataCompat.METADATA_KEY_DURATION);

                        if (positionBean.getCurPosition() == curPos && positionBean.getDuration() == duration) return;

                        positionBean.setCurPosition(curPos);
                        positionBean.setDuration(duration);
                        onPlayingPosBus.onNext(positionBean);

                        for (IAudioControllerListener listener :
                                controllerListenerList) {
                            listener.onAudioPosition(curPos, duration);
                        }
                    }
                })
                .subscribe();
    }

    /**
     * 切换歌曲时调用
     */
    private void playChanged(MediaMetadataCompat metadata) {
        String audioId = metadata.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID);
        for(int i = 0; i < curAudioItemList.size(); i++) {
            AudioItemData audioItemData = curAudioItemList.get(i);
            audioItemData.setSelect(audioId.equals(audioItemData.getAudioId()));
        }

        for (IAudioControllerListener listener :
                controllerListenerList) {
            listener.onPlayChange(metadata, curAudioItemList);
        }
    }

    /**
     * 监听控制器层与浏览器的回调
     */
    private void setListener() {
        connectionBuilder.setConnectionListener(new IServiceConnectionListener() {
                    @Override
                    public void onConnected(MediaControllerCompat mediaController) {
                        audioController = mediaController;
                        transportControls = mediaController.getTransportControls();
                        audioController.registerCallback(controllerCallback);

                        sendAudioPlayData(curRootId, curAudioItemList);

                        for (IAudioControllerListener listener :
                                controllerListenerList) {
                            listener.onConnected();
                        }
                    }

                    @Override
                    public void onFailure() {
                        for (IAudioControllerListener listener :
                                controllerListenerList) {
                            listener.onFailure();
                        }
                    }
                })
                .setAudioPlayingListener(new IAudioPlayingListener() {
                    @Override
                    public void onPlayingState(PlaybackStateCompat state) {
                        playingState = state;

                        for (IAudioControllerListener listener :
                                controllerListenerList) {
                            listener.onPlayingState(state);
                        }

                        if (isPlaying(state)) {
                            if (playingPosDisposable == null) {
                                checkPlaybackPosition();
                            }
                        } else {
                            closePlayingPos();
                        }
                    }

                    @Override
                    public void onPlayingData(MediaMetadataCompat data) {
                        curPlayingData = data;

                        for (IAudioControllerListener listener :
                                controllerListenerList) {
                            listener.onPlayingData(data);
                        }
                    }
                })
                .setSubscriptionCallback(new MediaBrowserCompat.SubscriptionCallback() {
                    @Override
                    public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaBrowserCompat.MediaItem> children) {
                        List<AudioItemData> audioItemList = new ArrayList<>();
                        for (int i = 0; i < children.size(); i++) {
                            MediaBrowserCompat.MediaItem mediaItem = children.get(i);
                            MediaDescriptionCompat description = mediaItem.getDescription();

                            AudioItemData audioItemData = new AudioItemData();
                            audioItemData.setAudioId(description.getMediaId());
                            CharSequence title = description.getTitle();
                            if (title != null) {
                                audioItemData.setName(title.toString());
                            }
                            Uri mediaUri = description.getMediaUri();
                            if (mediaUri != null) {
                                audioItemData.setPlayUrl(mediaUri.getPath());
                            }
                            audioItemData.setSelect(i == curItemIndex);

                            audioItemList.add(audioItemData);
                        }

                        for (IAudioSubscriptionCallback callback :
                                subscriptionCallbackList) {
                            callback.onSubscription(audioItemList);
                        }
                    }
                });
    }

    /**
     * 关闭获取音频进度监听
     */
    private void closePlayingPos() {
        if (playingPosDisposable != null) {
            playingPosDisposable.dispose();
            playingPosDisposable = null;
        }
    }

    /**
     * 控制器通过指令方式与服务通信的回调
     */
    private class ControllerResultReceiver extends ResultReceiver {

        public ControllerResultReceiver(Handler handler) {
            super(handler);
        }

        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            if (resultCode == AudioDataSource.RESULT_CODE_AUDIO_ITEM_INDEX) {
                curItemIndex = resultData.getInt(AudioDataSource.EXTRA_AUDIO_ITEM_INDEX);

            } else if (resultCode == AudioDataSource.RESULT_CODE_AUDIO_PLAY_CHANGE) {
                MediaMetadataCompat metadataCompat = resultData.getParcelable(AudioDataSource.EXTRA_AUDIO_PLAY_DATA);
                playChanged(metadataCompat);

            }
        }
    }

    /**
     * 控制器与服务通信回调
     */
    private final MediaControllerCompat.Callback controllerCallback = new MediaControllerCompat.Callback() {
        @Override
        public void onRepeatModeChanged(int repeatMode) {
            if (AudioController.this.repeatMode == repeatMode) return;

            AudioController.this.repeatMode = repeatMode;

            for (IAudioControllerListener listener :
                    controllerListenerList) {
                listener.onRepeatModeChanged(repeatMode);
            }
        }

        @Override
        public void onShuffleModeChanged(int shuffleMode) {
            if (AudioController.this.shuffleMode == shuffleMode) return;

            AudioController.this.shuffleMode = shuffleMode;

            for (IAudioControllerListener listener :
                    controllerListenerList) {
                listener.onShuffleModeChanged(shuffleMode);
            }
        }

        @Override
        public void onSessionDestroyed() {
            closePlayingPos();
            //断掉与服务的连接
            connection.disConnect();
        }

    };

}
